//
// Created by shen on 2025/3/22.
//

#include "FavFrameHelper.h"

FavFrameHelper::~FavFrameHelper() {

}

FavFrameHelper::FavFrameHelper(JavaCallHelper *_helper, const char *_url,
                               FFStatus *kzgPlayerStatus):
                               helper(_helper),playerStatus(kzgPlayerStatus) {
    strcpy(this->url,_url);
    this->queue = new SafeQueue(kzgPlayerStatus);
}

void FavFrameHelper::init() {
    decodeAvPacketThread= new std::thread(t_decode_avPack, this);

}
int avformat_ff_callback(void *ctx){
    FavFrameHelper *fFmpeg = (FavFrameHelper *) ctx;
    if(fFmpeg->playerStatus->exit)
    {
        return AVERROR_EOF;
    }
    return 0;
}
void FavFrameHelper::decodeAVPackate() {
    std::lock_guard<std::mutex> lock(mtx);
    avformat_network_init();
    avFormatContext=avformat_alloc_context();
    avFormatContext->interrupt_callback.callback = avformat_ff_callback;
    avFormatContext->interrupt_callback.opaque = this;
    int ret = avformat_open_input(&avFormatContext,url,nullptr,nullptr);

    LOGCATI("avformat_open_input = %s",url);
    if (ret != 0){
        FFERRORMESSAGE("");
        avformat_close_input(&avFormatContext);
        avformat_free_context(avFormatContext);
        avFormatContext= nullptr;
        isExit = true;
        return;
    }

    ret = avformat_find_stream_info(avFormatContext,0);

    if (ret != 0){
        FFERRORMESSAGE("");
        avformat_close_input(&avFormatContext);
        avformat_free_context(avFormatContext);
        avFormatContext= nullptr;
        isExit = true;
        return;
    }

    ret = -1;

    for (int i = 0; i < avFormatContext->nb_streams; ++i) {
        if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            avStreamIndex = i;
            time_base = avFormatContext->streams[i]->time_base;
            duration = avFormatContext->duration;
            LOGCATE("获取视频流成功 %d",avStreamIndex);
            break;
        }
    }


    if (getAvCodecContent(avFormatContext->streams[avStreamIndex]->codecpar,&avCodecContext) != 0){
        isExit = true;
        return;
    }

    avCodecContext->skip_loop_filter = AVDISCARD_NONKEY;
    helper->onGetFrameInitSuccess(
            avCodecContext->codec->name,
            avCodecContext->width,
            avCodecContext->height,
            avCodecContext->extradata_size,
            avCodecContext->extradata_size,
            avCodecContext->extradata,
            avCodecContext->extradata);
}
void FavFrameHelper::t_decode_avPack(FavFrameHelper *mFavFrameHelper) {
    mFavFrameHelper->decodeAVPackate();
}

int FavFrameHelper::getAvCodecContent(AVCodecParameters *avCodecParameters,
                                      AVCodecContext **avCodecContext) {
    std::lock_guard<std::mutex> lock(init_mutex);
    int ret;
    const AVCodec * avCodec = avcodec_find_decoder(avCodecParameters->codec_id);
    if (!avCodec){
        isExit = true;

        LOGCATI("find decoder fail %s:", url);
//        helper->onStatus(THREAD_CHILD,3);
        return -1;
    }

    *avCodecContext = avcodec_alloc_context3(avCodec);
    if (!*avCodecContext){
        isExit = true;
        LOGCATI("alloc codec context fail %s:", url);
//        helper->onStatus(THREAD_CHILD,3);
        return -1;
    }

    ret = avcodec_parameters_to_context(*avCodecContext,avCodecParameters);
    if (ret < 0){
        isExit = true;

        LOGCATI("avcodec_parameters_from_context fail %s:", url);
//        helper->onStatus(1005,"avcodec_parameters_from_context fail",THREAD_CHILD);
        return -1;
    }

    (*avCodecContext)->thread_type = FF_THREAD_FRAME;
    (*avCodecContext)->thread_count = 2;

    ret = avcodec_open2(*avCodecContext,avCodec,0);
    if (ret != 0){
        isExit = true;
        LOGCATI("open audio codec fail %s:", url);
//        helper->onError(1006,"open audio codec fail",THREAD_CHILD);
        return -1;
    }

    return 0;
}

void *FavFrameHelper::startGetFrame(FavFrameHelper *mFavFrameHelper) {
    mFavFrameHelper->decodeFrame();
}

void *FavFrameHelper::startPutAvPacket(FavFrameHelper *mFavFrameHelper) {
    mFavFrameHelper->decodeFrameFromQueue();
}
void FavFrameHelper::startAvFrame() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    if (playerStatus!= nullptr&&!playerStatus->exit){
        thread_start_get_frame=new std::thread(startGetFrame,this);
//        thread_start_decode_frame=new std::thread(startPutAvPacket,this);
    }

}

void FavFrameHelper::decodeFrameFromQueue() {
    LOGCATI("decodeFrameFromQueue");
    while (playerStatus != nullptr && !playerStatus->exit){
        if (!isPause){
            std::this_thread::sleep_for(std::chrono::seconds(1));
            continue;
        }

        AVPacket *avPacket = av_packet_alloc();

        if (queue->getAvPacket(avPacket)!= 0){
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = nullptr;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            continue;
        }

        std::lock_guard<std::mutex> lock(codecMutex);

        if (avcodec_send_packet(avCodecContext,avPacket)!= 0){
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = nullptr;
            continue;
        }
        AVFrame *avFrame = av_frame_alloc();
        if (avcodec_receive_frame(avCodecContext,avFrame)!= 0){
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = nullptr;
            continue;
        }
        LOGCATI("decodeFrameFromQueue 24");
        if (avFrame->format == AV_PIX_FMT_YUV420P || avFrame->format == AV_PIX_FMT_YUVJ420P){
            avFrame->pts=avFrame->best_effort_timestamp;
            int width = avFrame->linesize[0] > avCodecContext->width? avFrame->linesize[0]:avCodecContext->width;

            helper->onFrameYUVTobitmap(THREAD_CHILD, width,
                               avCodecContext->height,
                               avFrame->data[0],
                               avFrame->data[1],
                               avFrame->data[2],
                               avCodecContext->width,
                               (avFrame->pts *av_q2d(time_base) * AV_TIME_BASE));
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = nullptr;
        }else{
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = nullptr;
        }

        av_packet_free(&avPacket);
        av_free(avPacket);
        avPacket = nullptr;

    }
}
int getAvPacketRefType2(AVPacket *pAvPkt){
    unsigned char *pData = pAvPkt->data;
    unsigned char *pEnd = nullptr;
    int dataSize=pAvPkt->size;
    int curSize=0;
    int naluSize=0;
    int i;

    unsigned char nalHeader,nalType,refType;

    while(curSize<dataSize){
        if (pEnd-pData<4){
            goto fail;
        }
        for (i = 0; i < 4; ++i) {
            naluSize<<=8;
            naluSize|=pData[i];
        }

        pData += 4;


        if(naluSize > (pEnd-pData+1) || naluSize <= 0){
            goto fail;
        }
        nalHeader = *pData;
        nalType = nalHeader&0x1F;
        refType = (nalHeader >> 5 )&0x03;
        //LOGE("nalHeader:%d  ,nalType:%d, ref:%d",nalHeader,nalType,refType);
        if(nalType == 5){
            //IDR帧
            return 1;
        } else if (nalType == 1){
            //I,P,B帧
            return refType;
        } else{
            return 1;
        }

    }

    fail:
    return 0;



}
void FavFrameHelper::decodeFrame() {
    int ret;

    LOGCATI("decodeFrame begin getting frame");
    while (playerStatus != nullptr && !playerStatus->exit){

        if (!isPause || queue->getQueueSize() > 30){
            std::this_thread::sleep_for(std::chrono::seconds(1));
            continue;
        }


        AVPacket *avPacket=av_packet_alloc();
        ret = av_read_frame(avFormatContext,avPacket);

        if (ret!= 0){
            std::this_thread::sleep_for(std::chrono::seconds(1));
//           /**/ FFERRORMESSAGE("");
            FFERRORMESSAGE("");
            LOGCATI("av_read_frame = %d",ret);
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = nullptr;
            continue;
        }

        LOGCATI(" avPacket->stream_index = %d", avPacket->stream_index);
        if (avPacket->stream_index==0){
            LOGCATE("avPacket content %s", avPacket->data);
        }
        if (avPacket->stream_index!=avStreamIndex){
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = nullptr;
            continue;
        }else if (avPacket->stream_index==avStreamIndex){

            LOGCATE("avPacket content %s", avPacket->data);
            if (getAvPacketRefType2(avPacket)>0) {
                queue->putAvPacket(avPacket);
            }else{
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = nullptr;
            }

        }else{
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = nullptr;
        }
    }
    isExit = true;

}
